package com.netty.pool;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.pool.AbstractChannelPoolHandler;
import io.netty.channel.pool.AbstractChannelPoolMap;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.pool.FixedChannelPool;
import io.netty.channel.pool.SimpleChannelPool;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

import com.netty.exception.ConnectServerException;
import com.netty.handler.DefaultChannelPoolHandler;
/**
 * 默认channelpool配置
 * @author hmm
 *
 */
public class DefaultChannelPoolMap extends AbstractChannelPoolMap<InetSocketAddress, ChannelPool> {
	
	private final int maxConnections;
	private final int maxInitSize;
	private NioEventLoopGroup group = new NioEventLoopGroup();
	
	private final Map<InetSocketAddress, AbstractChannelPoolHandler> handlerMap = new HashMap<InetSocketAddress, AbstractChannelPoolHandler>();
	
	public DefaultChannelPoolMap(){
		this(10, 10);
	}
	/**
	 * @param maxConnections 最大连接数
	 * @param maxInitSize 最大初始化池数
	 */
	public DefaultChannelPoolMap(int maxConnections, int maxInitSize){
		this.maxConnections=maxConnections;
		this.maxInitSize = maxInitSize;
	}
	
	public void addHandler(InetSocketAddress addr, AbstractChannelPoolHandler channelPoolHandler){
		handlerMap.put(addr, channelPoolHandler);
	}
	
	@Override
	protected ChannelPool newPool(InetSocketAddress addr) {
		Bootstrap bootstrap = new Bootstrap();
		bootstrap.remoteAddress(addr);
		bootstrap.channel(NioSocketChannel.class);
		bootstrap.group(group);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.option(ChannelOption.TCP_NODELAY, true);
        bootstrap.option(ChannelOption.SO_SNDBUF, 32000);
        bootstrap.option(ChannelOption.SO_RCVBUF, 32000);
		bootstrap.option(ChannelOption.SO_TIMEOUT, 50);
		bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
		AbstractChannelPoolHandler channelPoolHandler = handlerMap.get(addr);
		if(null == channelPoolHandler){
			channelPoolHandler = new DefaultChannelPoolHandler();
		}
		SimpleChannelPool pool = new FixedChannelPool(bootstrap, channelPoolHandler, maxConnections);
		this.initChannelPool(pool, addr);
		return pool;
	}

	private void initChannelPool(final ChannelPool pool, final InetSocketAddress addr){
		for(int i=0;i < maxInitSize;i++){
			/**
			 * 这是一种不够严谨的方式，假如需要加入很多个连接，很可能前半部分的确在新建后释放了，
			 * 然而后半部分实际上并没有新建，而是取得前面的连接然后释放，这样就导致实际初始化的channel并没有达到500个，看源码很容易知道原因
			 * 当然，目前的测速中，我初始化1000个最大连接数，倒是还没有发生这种情况
			 */
			pool.acquire().addListener(new GenericFutureListener<Future<Channel>>() {
				@Override
				public void operationComplete(Future<Channel> future)
						throws Exception {
					if(future.isSuccess()){
						Channel channel = future.getNow();
						pool.release(channel);
					}else{
						throw new ConnectServerException(addr+" client not connect server");
					}
				}
			});
		}
	
	}

	public void shutdown(){
		group.shutdownGracefully();
	}
}
